package gpsdatadecryption;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.nio.ByteBuffer;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 *
 * @author Sam De Roeck
 */
public class SecurityManager {
    // static data
    // local keys
    public PublicKey publicBoxKey;
    private PrivateKey privateBoxKey;
    static final byte[] ID = ("SomeStaticKeyID").getBytes();
    // server key
    public PublicKey publicServerKey;
    // Nonce
    byte[] nonce;

    // constructor
    public SecurityManager() {
        GenerateOwnKeys();
    }

    // keypair generation
    private void GenerateOwnKeys() {

        try {

            // Generate Assymetric keypair via RSA with 2048 bits
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(2048);
            KeyPair kp = kpg.genKeyPair();

            // store keys
            publicBoxKey = kp.getPublic();
            privateBoxKey = kp.getPrivate();

        } catch (NoSuchAlgorithmException e) {
        }
    }

    // getters & setters
    public PublicKey getPublicBoxKey() {
        return publicBoxKey;
    }

    public void setPublicBoxKey(PublicKey publicBoxKey) {
        this.publicBoxKey = publicBoxKey;
    }

    // create nonce
    public byte[] createNonce() {
        byte[] N = new byte[16];

        try {
            Random rand;
            rand = SecureRandom.getInstance("SHA1PRNG");
            rand.nextBytes(N);

        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(SecurityManager.class.getName()).log(Level.SEVERE, null, ex);
        }
        return N;
    }

    public static String encryptRawGPSData(Long timestamp,Location location,String keyValue) throws Exception{
        Key key = new SecretKeySpec(keyValue.getBytes(), "AES");
        Cipher c = Cipher.getInstance("AES");
        c.init(Cipher.ENCRYPT_MODE, key);

        ByteBuffer bb = ByteBuffer.wrap(new byte[Long.SIZE + 2*Double.SIZE]);
        bb.putLong(timestamp);
        bb.putDouble(location.getCoordinates()[0]);
        bb.putDouble(location.getCoordinates()[1]);
        byte[] valueToEnc = bb.array();

        byte[] encValue = c.doFinal(valueToEnc);
        String encryptedValue = new BASE64Encoder().encode(encValue);
        return encryptedValue;

    }

    public static HashMap<Long, Location> decryptRawGPSData(String encrypted, String keyLocation) throws Exception {
        Key key = readSecretKeyFromFile(keyLocation);
        Cipher c = Cipher.getInstance("AES");
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decodedValue = new BASE64Decoder().decodeBuffer(encrypted);
        byte[] decValue = c.doFinal(decodedValue);

        ByteBuffer bb = ByteBuffer.wrap(decValue);
        long timestamp = bb.getLong();
        double lat = bb.getDouble();
        double lon = bb.getDouble();

        HashMap<Long, Location> returnHash = new HashMap<Long, Location>();
        returnHash.put(timestamp, new Location(lat, lon));
        return returnHash;
    }

    private static SecretKeySpec readSecretKeyFromFile(String fileName) throws IOException{
        ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(fileName)));
        try {
            SecretKeySpec key = (SecretKeySpec)oin.readObject();
            return key;
        } catch (Exception e) {
            throw new RuntimeException("Spurious serialisation error", e);
        } finally{
            oin.close();
        }
    }
}
